<?php

namespace MathPHP\Tests\Functions\Special;

use MathPHP\Functions\Special;
use MathPHP\Exception;

/**
 * Tests for Airy functions
 */
class AiryTest extends \PHPUnit\Framework\TestCase
{
    /**
     * @test Airy function Ai(x)
     * @dataProvider dataProviderForAiryAiSmallAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryAiSmallAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyAi($x);

        // Then for |x| < 10, Bessel relationships provide ~1e-8 precision
        $tolerance = \abs($expected) * 1e-8 + 1e-12;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryAiSmallAbsoluteValues(): array
    {
        return [
            [-9.0, -0.02213372154734113],
            [-8.350834392078323, -0.3043925336916225],
            [-8.314214058591274, -0.2890613612782382],
            [-7.830310281618491, 0.10600182897405509],
            [-7.0, 0.1842808352505062],
            [-5.345526268252332, 0.1472733565666057],
            [-5.0183952461055, 0.3444468641382208],
            [-5.0, 0.3507610090241143],
            [-4.0, -0.07026553294928964],
            [-2.7221992543153695, -0.25288402141245],
            [-2.0, 0.22740742820168564],
            [-1.7572006313185629, 0.3620201071013304],
            [-1.0, 0.5355608832923522],
            [-0.5, 0.4757280916105395],
            [0.0, 0.3550280538878172],
            [0.5, 0.23169360648083343],
            [0.9902572652895136, 0.13684937225869992],
            [1.0, 0.13529241631288147],
            [2.0, 0.03492413042327436],
            [3.0, 0.00659113935746072],
            [3.946339367881464, 0.0010623269692673857],
            [4.044600469728352, 0.0008678933149937907],
            [4.474115788895179, 0.00034933806157038067],
            [5.0, 0.00010834442813607433],
            [6.0, 9.947694360252897e-06],
            [8.0, 4.692207616099224e-08],
            [8.32290311184182, 1.8474341125374925e-08],
            [9.279757672456203, 1.0527872514277917e-09],
        ];
    }

    /**
     * @test Airy function Ai(x)
     * @dataProvider dataProviderForAiryAiLargeAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryAiLargeAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyAi($x);

        // Then for |x| ≥ 10, asymptotic expansions provide ~1e-4 relative precision
        $tolerance = \abs($expected) * 1e-4 + 1e-9;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryAiLargeAbsoluteValues(): array
    {
        return [
            [-25.0, 1.635265788304304e-01],
            [-20.0, -0.17640612707798434],
            [-19.176620228167902, 0.058800505798565765],
            [-19.0, -0.14166127688042127],
            [-17.67665551327202, 0.017747886275407826],
            [-17.0, -0.10526230029095027],
            [-15.0, 0.27821749087082903],
            [-14.420245573918326, -0.11522347529440936],
            [-14.0, -0.26598348278407785],
            [-13.760219186551893, -0.07378955186002939],
            [-13.75925438230254, -0.07277576823290736],
            [-12.727001311715975, -0.10629369577539208],
            [-12.663819605862647, -0.166133339279367],
            [-12.0, -0.06655517505437264],
            [-11.506435572868954, 0.3047857139158642],
            [-10.0, 0.04024123848644196],
            [10.0, 1.1047532552898654e-10],
            [11.0, 4.226275864960366e-12],
            [13.0, 3.98177607883335e-15],
            [13.29770563201687, 1.345309984092537e-15],
            [14.647045830997406, 8.475713130672701e-18],
            [15.0, 2.1649625207379936e-18],
            [16.0, 4.1568888289170346e-20],
            [18.0, 1.060046682524804e-23],
            [18.028572256396647, 9.386146142268405e-24],
            [18.796394086479772, 3.4446695996114927e-25],
            [20.0, 1.691672868670544e-27],
        ];
    }

    /**
     * @test Airy function Bi(x)
     * @dataProvider dataProviderForAiryBiSmallAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryBiSmallAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyBi($x);

        // Then for |x| < 10, Bessel relationships provide ~1e-9 precision
        $tolerance = \abs($expected) * 1e-9 + 1e-12;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryBiSmallAbsoluteValues(): array
    {
        return [
            [-9.0, 0.32494732345524496],
            [-8.350834392078323, -0.13215894307805431],
            [-8.314214058591274, -0.16372596453249236],
            [-7.830310281618491, -0.3201238553004193],
            [-7.0, 0.2937620718544136],
            [-5.345526268252332, -0.3403641778577927],
            [-5.0183952461055, -0.15256698402563473],
            [-5.0, -0.1383691349016008],
            [-4.0, 0.39223470570699925],
            [-2.7221992543153695, -0.35730601616676794],
            [-2.0, -0.4123025879563985],
            [-1.7572006313185629, -0.3227468212152226],
            [-1.0, 0.10399738949694468],
            [-0.5, 0.3803526597510537],
            [0.0, 0.6149266274460007],
            [0.5, 0.8542770431031554],
            [0.9902572652895136, 1.1983960952036112],
            [1.0, 1.2074235949528715],
            [2.0, 3.2980949999782143],
            [3.0, 14.037328963730229],
            [3.946339367881464, 75.6225742535795],
            [4.044600469728352, 91.4137526919247],
            [4.474115788895179, 215.78131833882588],
            [5.0, 657.7920441711713],
            [6.0, 6536.446104809864],
            [8.0, 1199586.0041244598],
            [8.32290311184182, 2986979.1466049245],
            [9.279757672456203, 49635967.83912318],
        ];
    }

    /**
     * @test Airy function Bi(x)
     * @dataProvider dataProviderForAiryBiLargeAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryBiLargeAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyBi($x);

        // Then for |x| ≥ 10, asymptotic expansions provide ~1e-4 relative precision
        $tolerance = \abs($expected) * 1e-4 + 1e-9;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryBiLargeAbsoluteValues(): array
    {
        return [
            [-25.0, -0.1921468156903772],
            [-20.0, -0.20013930932265164],
            [-19.176620228167902, 0.26311427324704084],
            [-19.0, 0.23012109009458917],
            [-17.67665551327202, 0.27457661934630967],
            [-17.0, -0.2571359210023441],
            [-15.0, -0.06912659453100989],
            [-14.420245573918326, 0.2655980116120777],
            [-14.0, -0.11966555279762447],
            [-13.760219186551893, -0.2834781719431154],
            [-13.75925438230254, -0.28374542584778123],
            [-12.727001311715975, 0.27914155422253134],
            [-12.663819605862647, 0.24867700742356372],
            [-12.0, -0.29571991207807313],
            [-11.506435572868954, -0.03056596710634996],
            [-10.0, -0.3146798296438388],
            [10.0, 455641153.54822654],
            [11.0, 11355782530.430458],
            [13.0, 11086706719059.367],
            [13.29770563201687, 32444292717267.973],
            [14.647045830997406, 4906711027280349.0],
            [15.0, 1.8982099567493584e+16],
            [16.0, 9.572123906049167e+17],
            [18.0, 3.5389182503565394e+21],
            [18.028572256396647, 3.993592422564895e+21],
            [18.796394086479772, 1.0657258798563945e+23],
            [20.0, 2.1037650496511e+25],
        ];
    }

    /**
     * @test Airy function derivative Ai'(x)
     * @dataProvider dataProviderForAiryAipSmallAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryAipSmallAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyAip($x);

        // Then for |x| < 10, Bessel relationships provide ~1e-9 precision
        $tolerance = \abs($expected) * 1e-7 + 1e-12;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryAipSmallAbsoluteValues(): array
    {
        return [
            [-9.0, -0.9756639809263316],
            [-8.350834392078323, 0.3729141381317562],
            [-8.314214058591274, 0.46354346645644795],
            [-7.830310281618491, 0.8994600264653922],
            [-7.0, -0.7710081684101255],
            [-5.345526268252332, 0.7945705857222674],
            [-5.0183952461055, 0.359227277401417],
            [-5.0, 0.3271928185544436],
            [-4.0, -0.7906285753685814],
            [-2.7221992543153695, 0.5711719119345766],
            [-2.0, 0.618259020741691],
            [-1.7572006313185629, 0.483244694641838],
            [-1.0, -0.010160567116645175],
            [-0.5, -0.2040816703395474],
            [0.0, -0.2588194037928068],
            [0.5, -0.224910532664684],
            [0.9902572652895136, -0.1604666632371849],
            [1.0, -0.15914744129679328],
            [2.0, -0.05309038443365388],
            [3.0, -0.011912976705951315],
            [3.946339367881464, -0.0021730935274801404],
            [4.044600469728352, -0.0017955614376768013],
            [4.474115788895179, -0.0007573206590485018],
            [5.0, -0.00024741389086846237],
            [6.0, -2.4765200397034972e-05],
            [8.0, -1.3414392979067844e-07],
            [8.32290311184182, -5.383874924955597e-08],
            [9.279757672456203, -3.2348398763311217e-09],
        ];
    }

    /**
     * @test Airy function derivative Ai'(x)
     * @dataProvider dataProviderForAiryAipLargeAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryAipLargeAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyAip($x);

        // Then for |x| ≥ 10, asymptotic expansions provide ~1.1e-4 relative precision
        $tolerance = \abs($expected) * 1.1e-4 + 1e-9;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryAipLargeAbsoluteValues(): array
    {
        return [
            [-25.0, 9.623788513876932e-01],
            [-20.0, 0.8928628567364726],
            [-19.176620228167902, -1.1514658741886088],
            [-19.0, -1.004961125005143],
            [-17.67665551327202, -1.1542010359005148],
            [-17.0, 1.0586845766446638],
            [-15.0, 0.27237420430864134],
            [-14.420245573918326, -1.0106313183974012],
            [-14.0, 0.4430248770028435],
            [-13.760219186551893, 1.05027857179744],
            [-13.75925438230254, 1.0512514333884226],
            [-12.727001311715975, -0.9979977092607727],
            [-12.663819605862647, -0.8882946297572563],
            [-12.0, 1.0231104533679711],
            [-11.506435572868954, 0.11031186119150452],
            [11.0, -1.411144124662854e-11],
            [-10.0, 0.9962650441327905],
            [10.0, -3.520633676738912e-10],
            [13.0, -1.4432080573972682e-14],
            [13.29770563201687, -4.930785774503677e-15],
            [14.647045830997406, -3.258088530120278e-17],
            [15.0, -8.420567954017777e-18],
            [16.0, -1.669188676838185e-19],
            [18.0, -4.512001860681978e-23],
            [18.028572256396647, -3.998275277452225e-23],
            [18.796394086479772, -1.497976951082438e-24],
            [20.0, -7.586391625748372e-27],
        ];
    }

    /**
     * @test Airy function derivative Bi'(x)
     * @dataProvider dataProviderForAiryBipSmallAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryBipSmallAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyBip($x);

        // Then for |x| < 10, Bessel relationships provide ~1e-8 precision
        $tolerance = \abs($expected) * 1e-8 + 1e-12;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryBipSmallAbsoluteValues(): array
    {
        return [
            [-9.0, -0.05740051384366842],
            [-8.350834392078323, -0.883812571112477],
            [-8.314214058591274, -0.8386308843337057],
            [-7.830310281618491, 0.2865165169037395],
            [-7.0, 0.49824459005811506],
            [-5.345526268252332, 0.3250182051958477],
            [-5.0183952461055, 0.765005263003866],
            [-5.0, 0.778411773001899],
            [-4.0, -0.11667056743834117],
            [-2.7221992543153695, -0.4516960982591664],
            [-2.0, 0.27879516692116973],
            [-1.7572006313185629, 0.4484397245746799],
            [-1.0, 0.5923756264227923],
            [-0.5, 0.5059337136238472],
            [0.0, 0.4482883573538264],
            [0.5, 0.5445725641405924],
            [0.9902572652895136, 0.9207734129155632],
            [1.0, 0.9324359333927756],
            [2.0, 4.10068204993289],
            [3.0, 22.92221496638217],
            [3.946339367881464, 144.94121301291705],
            [4.044600469728352, 177.63805102929032],
            [4.474115788895179, 443.3935291012377],
            [5.0, 1435.8190802179822],
            [6.0, 15725.602621930475],
            [8.0, 3354342.3127445388],
            [8.32290311184182, 8525049.084416496],
            [9.279757672456203, 149836045.13026842],
        ];
    }

    /**
     * @test Airy function derivative Bi'(x)
     * @dataProvider dataProviderForAiryBipLargeAbsoluteValues
     * @param float $x
     * @param float $expected
     */
    public function testAiryBipLargeAbsoluteValues(float $x, float $expected)
    {
        // When
        $result = Special::airyBip($x);

        // Then for |x| ≥ 10, asymptotic expansions provide ~1.1e-4 relative precision
        $tolerance = \abs($expected) * 1.1e-4 + 1e-9;
        $this->assertEqualsWithDelta($expected, $result, $tolerance);
    }

    /**
     * Test data from SciPy scipy.special.airy()
     */
    public function dataProviderForAiryBipLargeAbsoluteValues(): array
    {
        return [
            [-25.0, 8.157197157546104e-01],
            [-20.0, -0.7914290338395352],
            [-19.176620228167902, 0.260929380105104],
            [-19.0, -0.614473753956068],
            [-17.67665551327202, 0.07850330335671052],
            [-17.0, -0.4378020657909788],
            [-15.0, 1.076429753084375],
            [-14.420245573918326, -0.4329692141039689],
            [-14.0, -0.997411818949334],
            [-13.760219186551893, -0.2788855075127973],
            [-13.75925438230254, -0.2751204285446287],
            [-12.727001311715975, -0.37374986560030315],
            [-12.663819605862647, -0.5863448984284145],
            [-12.0, -0.23673219783112162],
            [-11.506435572868954, 1.0333099062186013],
            [-10.0, 0.11941411339990535],
            [10.0, 1429236134.48287],
            [11.0, 37400168196.926926],
            [13.0, 39757544969908.22],
            [13.29770563201687, 117693342844292.42],
            [14.647045830997406, 1.8693990057830964e+16],
            [15.0, 7.319749203407008e+16],
            [16.0, 3.8137435071218534e+18],
            [18.0, 1.4964796503287732e+22],
            [18.028572256396647, 1.6900979940616472e+22],
            [18.796394086479772, 4.606148751740744e+23],
            [20.0, 9.381839336133948e+25],
        ];
    }
}
